;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;
;
;
;
;
;
;
;




(define test101 '((B C D) (A F) (G H) (M) (O A) (WHY IS IT T)(B C) (A)))

(define test1020 '(AND B   (OR (NOT N) (NOT B) K (NOT B)) (OR O P) (OR (NOT N)  (NOT B)) (OR N G)  (OR G H M) (OR G K) 
	(OR M N A G H) (OR (NOT A) N (NOT B) C D P Q R)))

(define test1021 '(AND B  (OR (NOT B) c ) ))

(load "utility.txt")
(load "testcases.txt")

(define gVarlist '());global variable


(define setsymbol
	(lambda (x value)
		(set! gVarlist (dosetsymbol x value gVarlist))
	)
)

(define dosetsymbol
	(lambda (x value varlist)
		(if (null? varlist)
			(list (cons x value))
			(if (equal? x (car(car varlist)))
				(cons (cons x value) (cdr varlist))
				(cons (car varlist) (dosetsymbol x value (cdr varlist)))
			)
		)
	)
)

(define getsymbol
	(lambda (x)
		(cdr (assoc x gVarlist))
	)
)


;helper function to do linear probing
(define doprobelist
	(lambda (f)
		(lambda (ls1 ls2)
			;(begin (pp (list "doprobelist ls1=" ls1 "ls2=" ls2))			
			(if (null? ls2)
				'()
				(if (null? (car ls2));ignore null
					((doprobelist f) ls1 (cdr ls2))
					(if (not(f (car ls2)))
						((doprobelist f)(append ls1 (list (car ls2))) (cdr ls2))
						(car ls2)
					)
				)
			)
			;)
		)
	)
)
			
;This is the linear probing function skeleton
;it takes two function as parameter, the first is a test function which takes a list as parameter
;the second is an exec function which also takes the returning list from "doprobelist" as first
;parameter, retrieve necessary information and exec another list which is its second parameter
;the returning of probelist is the processed list

(define probelist
	(lambda (ftest fexec)
		(lambda (ls)
			;(begin (pp (list "probelist ls=" ls))
			(if (boolean? ls)
				ls
				(let ((result ((doprobelist ftest)'() ls)))
					(if  (null? result)
						ls						
						((probelist ftest fexec)((fexec result) ls))
					)
				)
			)
			;)
		)
	)
)

(define is_negate
	(lambda (x y)
		;(begin (pp (list "is_negate x=" x "y=" y))
		(cond
			((and(symbol? x)(symbol? y)) #f)
			((and(list? x)(list? y)) #f)
			((and(is_not x)(symbol? y))(eq? (cadr x) y))
			((and(symbol? x)(is_not y))(eq? x (cadr y)))
			(else #f)
		)
		;)
	)
)



	
;(define mytest ((checktermwithsymbol '(NOT A)) '(AND B C A)))
;(define mytest1 ((checktermwithsymbol  'A)  '(OR N (NOT B) (NOT A) xC D P Q R)))
;(define mytest2 ((removesymbol  '(NOT A))  '(OR N (NOT B) (NOT A) xC D P Q R)))
;(define test1030 '(AND 

(define getnegatefromsymbol
	(lambda (ls)
		(if (is_not ls)
			(cadr ls)
			(list 'NOT ls)
		)
	)
)	


(define clearsymbol
	(lambda (sym)
		(if (is_symbol sym)
			sym
			(if (is_or sym)
				(cadr sym)
				(pp "error")
			)
		)
	)
)

;			
;process each term and try to detecting if the term contains either the variable itself or
;its negated format. In first case, it remove entire term to empty list. In second case,
; it only removes the negated format of variable alone.
(define mychecktermwithsymbol
	(lambda (sym)
		(lambda (ls)
			(begin ;(pp (list "mychecktermwithsymbol sym=" sym ))
			(let ((sym (clearsymbol sym)))
				(begin ;(pp(list "mychecktermwithsymbol sym=" sym "ls=" ls))
				(if (null? ls)
					'()
					(if (boolean? ls)
						(pp "mygod!")
					(if (is_symbol ls)
						(if (equal? sym ls)
							#t
							(if (is_negate sym ls)
								(begin ;(pp (list "is_negate sym=" sym))
									#f
								)
								ls
							)
						)						
						(if (equal? sym (car ls))
							#t
							;;you need wait the result from child
							(let ((result ((mychecktermwithsymbol sym)(cdr ls))))
								(if (boolean? result)
									result ;;means you find the same
									(if (is_negate sym (car ls))
										result ;;you remove the negate
										(cons (car ls) result)
									)
								)
							)
						)
					))
				))
			))
		)
	)
)



(define checkandsetsymbol
	(lambda (sym)
		(begin ;(pp (list "checkandsetsymbol sym=" sym))
		;(if (or(is_and sym)(is_or sym))
			;(checkandsetsymbol (cdr sym))
		(if (is_not sym)
			(let ((sym (cadr sym)))
				(let ((result (getsymbol sym)))
					(if (eq? 'N result)
						(begin (setsymbol sym #f)
							;(pp (list "within checkand setsymbol sym=" sym"gVarlist" gVarlist))
							;(setcurlist sym)
							#t
						)										
						(if result
							;contradiction
							#f
							#t	
						)
						;else do nothing
					)
				)
				
			)
			(let ((result (getsymbol sym)))
				(if (eq? 'N result)
					(begin  (setsymbol sym #t) 
						;(setcurlist sym)
						#t
					)
					(if (not result)
						#f
						#t
					);else do nothing
				)
			)
		))
	)
)
		
(define testbooleanfalse
	(lambda (term)
		(and (boolean? term)(not term))
	)
)
(define testbooleantrue
	(lambda (term)
		(if (and (boolean? term) term)
			'()
			term
		)
	)
)

(define wrapmysmartconstruct
	(lambda (term ls)
		(if (or (testbooleanfalse term)(testbooleanfalse ls))
			#f
			(mysmartconstruct (testbooleantrue term)(testbooleantrue ls))
		)
	)
)

;reconstruct list by removing both empty list and removing redundent variable. (Only one repetition
;is expected) It also helps to reveal those hidden single-variable clause, like (OR A) which is ;hidden single variable term
;

(define mysmartconstruct
	(lambda (term ls)
		(if (null? term)
			ls
			(if (is_symbol term)
				(cons term ls)
				(if (and (= 3 (length term))(equal? (cadr term)(caddr term)))
					(cons (cadr term) ls)					
					(if (= 2 (length term))
						(cons (cadr term) ls)
						(if (= 1 (length term));a single (or)
							ls
							(cons term ls)
						)
					)
				)
			)
		)
	)
)

(define wrapshrink_catch '())

(define wrapshrinklistwithsymbol
	(lambda (sym)
		(lambda (ls)
			(begin (set! wrapshrink_catch (list "wrapshrinkwithsymbol sym=" sym "ls=" ls))
			(if (checkandsetsymbol sym)
				((shrinklistwithsymbol sym)ls)
				#f
			))
		)
	)
)

(define catch '())

(define docompare
	(lambda (x ls)
		(if (null? ls)
			(begin
				(pp (list "difference: " x))
				'()
			)
			(if (equal? x (car ls))
				'()
				(docompare x (cdr ls))
			)
		)
	)
)

(define diff
	(lambda (ls1 ls2)
		(begin
			(compare ls1 ls2)
			(compare ls2 ls1)
		)
	)
)

(define compare
	(lambda (ls1 ls2)
		(if (null? ls1)
			'()
			(begin 
				(docompare (car ls1) ls2)
				(compare (cdr ls1) ls2)
			)
		)
	)
)

(define shrink_catch '())
(define shrinklistwithsymbol
	(lambda (sym)
		(lambda (ls)
			(begin ;(set! shrink_catch (list "shrinklistwithsymbol sym=" sym "ls=" ls))
				;(set! catch (list "shrinklistwithsymbol sym=" sym))
			(if (null? ls)
				'()
				(if (boolean? ls)
					ls
					(wrapmysmartconstruct ((mychecktermwithsymbol sym) (car ls))
						((shrinklistwithsymbol sym)(cdr ls)))
				)
			))
		)
	)
)

(define simplifylist
	(lambda (ls)
		((probelist issingleterm wrapshrinklistwithsymbol) ls)
	)
)


(define simplifylistwithoutset
	(lambda (ls)
		((probelist issingleterm shrinklistwithsymbol) ls)
	)
)

(define getargument
	(lambda (x)
		(if (cdr x)
			(car x)
			(getnegatefromsymbol (car x))
		)
	)
)

;(define demo (setsymbolandsimplify (cdr test) input))

(define setsymbolandsimplify
	(lambda (ls argument)
		(if (null? argument)
			(begin ;(pp(list "after set ls=" ls))
				ls
			)
			(let ((result  (simplifylistwithoutset
						((shrinklistwithsymbol (getargument(car argument))) ls)) ))
					(setsymbolandsimplify result (cdr argument))
			)
		)
	)
)

(define mylist '())
(define buffer1 '())
(define buffer2 '())

(define mycall
	(lambda (ls)
		(if (null? ls)
			mylist
			(begin
				(let ((result (demo (car (car ls)))))
					(if (boolean? result)
						(pp "oh")
						(mycall (cdr ls))
					)
				)
			)
		)
	)
)



(define demo
	(lambda (x)
		(begin (pp (list "now simplify x=" x ))
			;(if (eq? x 'A45)
				;#t
				(begin
					(set! buffer2 buffer1)
					;(pp " it is done!")
					(set! buffer1 mylist)
					(set!  mylist (simplifylistwithoutset ((shrinklistwithsymbol x) buffer2)))
					;(if (eq? x 'A1)
						;'()
				)
			;)
			;(pp (list "after simplify mylist is" mylist ))
		)
	)
)

		
(define issingleterm 
	(lambda (ls)
		(if (null? ls)
			#f
			(if (symbol? ls)
				(not (or (eq? ls 'AND)(eq? ls 'OR)))
				(if (is_not ls)
					#t
					(if (and (is_or ls)(= 2 (length ls)))
						#t
						(if (and (is_or ls)(= 3 (length ls))(equal? (cadr ls)(caddr ls)))
							#t
							#f
						)
					)
				)
			)
		)
	)
)




					
						
